package com.liiiyian.testitem.listener;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.fastjson2.JSON;
import com.liiiyian.testitem.domain.Table1;
import com.liiiyian.testitem.service.Table1Service;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.Date;

/**
 * @author LiiiYiAn
 * @version 1.0
 * @createDate 2024-06-05 16:27
 * @projectName testItem
 * @className EasyExcelListener
 * @description EasyExcel监听器，读数据（不能被Spring管理，只能通过new创建,然后里面用到Spring可以构造方法传进去）
 */
@Slf4j
public class EasyExcelListener implements ReadListener<Table1> {

    /**
     * 缓存量，每隔5条存储数据库，实际使用中可以100条，然后清理list ，方便内存回收
     */
    private static final int COUNT = 100;

    private Table1Service table1Service;

    private ArrayList<Table1> list = new ArrayList<>(COUNT);

    /**
     * 如果使用了Spring，就要加上构造方法。每次创建Listener的时候把Spring管理的类传进来
     */
    public EasyExcelListener(Table1Service table1Service) {
        this.table1Service = table1Service;
    }

    /**
     * @param data
     * @param context
     * @author LiiiYiAn
     * @createDate 2024/6/5 19:03
     * @description 每读一条数据后调用该方法
     **/
    @Override
    public void invoke(Table1 data, AnalysisContext context) {
        // Excel表中如果没有主键字段，则MyBatis Plus的插入操作会自动根据雪花算法生成主键
        log.info("解析到一条数据:{}", JSON.toJSONString(data));

        // 数据校验


        // 如果Excel表中没有创建时间字段，则根据当前时间自动生成
        if (data.getCreatetime() == null) {
            data.setCreatetime(new Date());
        }

        // 将读取到的一行数据添加到集合
        list.add(data);

        // 判断是否到达设置的缓存量
        if (list.size() >= COUNT) {
            // 将结果存储到数据库，防止数据几万条数据在内存，容易OOM
            // 调用MyBatis Plus内置批量插入方法
            table1Service.saveBatch(list);

            // 存储完成后清空 list
            list = new ArrayList<>(COUNT);
        }
    }


    /**
     * @author LiiiYiAn
     * @createDate 2024/6/6 10:56
     * @description 在转换异常 获取其他异常下会调用本接口。抛出异常则停止读取。如果这里不抛出异常则 继续读取下一行
     * @param exception
     * @param context
     **/
    @Override
    public void onException(Exception exception, AnalysisContext context) {
        log.error("解析失败，但是继续解析下一行:{}", exception.getMessage());
        // 如果是某一个单元格的转换异常 能获取到具体行号
        // 如果要获取头的信息 配合invokeHeadMap使用
        if (exception instanceof ExcelDataConvertException) {
            ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception;

            log.error("第{}行，第{}列解析异常，数据为:{}", excelDataConvertException.getRowIndex(),
                    excelDataConvertException.getColumnIndex(), excelDataConvertException.getCellData());
        }
    }


    /**
     * @param context
     * @author LiiiYiAn
     * @createDate 2024/6/5 19:03
     * @description 读完整个Excel数据后调用该方法
     **/
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        if (list.size() > 0) {
            // 将最后遗留的数据list也存储到数据库（因为在invoke()中，设置了每存储100条数据将其存储到数据库后会清空）
            table1Service.saveBatch(list);

            // 存储完成后清空 list
            list = new ArrayList<>(COUNT);
        }

        log.info("所有数据解析完成！");
    }

}
